package com.osdp.auth.server.impl;

import com.osdp.auth.server.ITokenService;
import com.osdp.common.constant.CommonConstant;
import com.osdp.common.exception.PlatformException;
import com.osdp.common.util.ConvertUtil;
import com.osdp.common.util.JWTUtil;
import com.osdp.common.util.MessageUtil;
import com.osdp.common.util.TokenUtil;
import com.osdp.common.base.JWTInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class TokenServiceImpl implements ITokenService {

    private static final long EXPIRE_TIME = 30 * 60 * 1000;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 存入redis
     * @param token
     */
    private void save(String token) {
        redisTemplate.opsForValue().set(CommonConstant.PREFIX_USER_TOKEN + token, token, 30, TimeUnit.MINUTES);
    }

    @Override
    public String create(JWTInfo info) {
        String token = null;
        try {
            token = TokenUtil.createToken(info);
        } catch (Exception e) {
            if(log.isErrorEnabled()){
                log.error(e.getMessage(),e);
            }
            throw new PlatformException(MessageUtil.getMessage(10036));
        }
        save(token);
        return token;
    }

    @Override
    public boolean verify(String token) {
        String cacheToken = String.valueOf(redisTemplate.opsForValue().get(CommonConstant.PREFIX_USER_TOKEN + token));
        if(StringUtils.isEmpty(cacheToken)||StringUtils.equals("null",cacheToken.trim())){
            return false;
        }
        return true;
    }

    @Override
    public boolean refresh(String token) {
        if(verify(token)){
            //expire 重新设置过期时间为30分钟，刷新时间
            Boolean expire = redisTemplate.expire(CommonConstant.PREFIX_USER_TOKEN + token, 30, TimeUnit.MINUTES);
            return expire != null && expire;
        }
        return false;
    }

    @Override
    public boolean invalid(String token) {
        String key = CommonConstant.PREFIX_USER_TOKEN + token;
        String cacheToken = String.valueOf(redisTemplate.opsForValue().get(key));
        if(StringUtils.isEmpty(cacheToken)||StringUtils.equals("null",cacheToken.trim())){
            return true;
        }
        redisTemplate.delete(key);
        return true;
    }

    @Override
    public boolean verifyAndRefresh(String token) {
        return verify(token) && refresh(token);
    }

    /**
     * JWTToken刷新生命周期 （解决用户一直在线操作，提供Token失效问题）
     * 1、登录成功后将用户的JWT生成的Token作为k、v存储到cache缓存里面(这时候k、v值一样)
     * 2、当该用户再次请求时，通过JWTFilter层层校验之后会进入到doGetAuthenticationInfo进行身份验证
     * 3、当该用户这次请求JWTToken值还在生命周期内，则会通过重新PUT的方式k、v都为Token值，缓存中的token值生命周期时间重新计算(这时候k、v值一样)
     * 4、当该用户这次请求jwt生成的token值已经超时，但该token对应cache中的k还是存在，则表示该用户一直在操作只是JWT的token失效了，程序会给token对应的k映射的v值重新生成JWTToken并覆盖v值，该缓存生命周期重新计算
     * 5、当该用户这次请求jwt在生成的token值已经超时，并在cache中不存在对应的k，则表示该用户账户空闲超时，返回用户信息已失效，请重新登录。
     * 6、每次当返回为true情况下，都会给Response的Header中设置Authorization，该Authorization映射的v为cache对应的v值。
     * 7、注：当前端接收到Response的Header中的Authorization值会存储起来，作为以后请求token使用
     * 参考方案：https://blog.csdn.net/qq394829044/article/details/82763936
     *
     * @param token
     * @param info
     * @param passWord
     * @return
     */
    @Override
    public boolean jwtTokenRefresh(String token, JWTInfo info, String passWord) {
        String cacheToken = String.valueOf(redisTemplate.opsForValue().get(CommonConstant.PREFIX_USER_TOKEN + token));
        if (ConvertUtil.isNotEmpty(cacheToken)) {
            // 校验token有效性
            if (!JWTUtil.verify(token, info, passWord)) {
                String newAuthorization = JWTUtil.sign(info, passWord);
                redisTemplate.opsForValue().set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);
                // 设置超时时间
                redisTemplate.expire(CommonConstant.PREFIX_USER_TOKEN + token, 2, TimeUnit.MINUTES);
            } else {
                redisTemplate.opsForValue().set(CommonConstant.PREFIX_USER_TOKEN + token, cacheToken);
                // 设置超时时间
                redisTemplate.expire(CommonConstant.PREFIX_USER_TOKEN + token, 2, TimeUnit.MINUTES);
            }
            return true;
        }
        return false;
    }
}
